﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using GeneralDFS.BusinessLogical.Config;
using GeneralDFS.Common;
using GeneralDFS.Common.Network;
using GeneralDFS.DataEntity.IO;
using GeneralDFS.DataEntity.Config;

namespace GeneralDFS.BusinessLogical.Network
{
    public class AsyncClientTcpClient
    {
        private readonly TcpClient _client;
        private DFSConfig config;
        public delegate void OnUploadProgressBegin(object sender, UploadProgressChangedEventArgs eventArgs);
        public delegate void OnUploadProgressChanged(object sender, UploadProgressChangedEventArgs eventArgs);
        public delegate void OnUploadProgressCompleted(object sender, UploadProgressChangedEventArgs eventArgs);

        public event OnUploadProgressBegin UploadProgressBegin;
        public event OnUploadProgressChanged UploadProgressChanged;
        public event OnUploadProgressCompleted UploadProgressCompleted;

        public UploadProgressChangedEventArgs eventArgs;

        public AsyncClientTcpClient(DFSConfig config, String ip, int port)
        {
            this.config = config;
            _client = new TcpClient();
            _client.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
        }

        /// <summary>
        /// 异步写入
        /// </summary>
        /// <param name="metaData"></param>
        /// <param name="filePath"></param>
        public void Write(FileBlockMetaData metaData, String filePath)
        {
            var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
            fileStream.Seek(metaData.Offset, SeekOrigin.Begin);
            if (fileStream.Length - fileStream.Position < config.DetailConfig.BlockSize)
                metaData.Length = fileStream.Length - fileStream.Position;
            else
                metaData.Length = config.DetailConfig.BlockSize;

            var state = new CustomTcpState
                {
                    NetworkStream = _client.GetStream(),
                    FileStream = fileStream,
                    FileBlockMetaData = metaData,
                    Buffer = new Byte[config.DetailConfig.BlockSize],
                    PreBuffer = new byte[config.DetailConfig.BlockSize],
                };
            state.BufferSize = state.Buffer.Length;
            state.PreBufferSize = state.PreBuffer.Length;
            Byte[] metaDataBytes = SerializeHelper.GetBytes(metaData);
            Byte[] metaDataLengthBytes = BitConverter.GetBytes(metaDataBytes.Length);
            state.NetworkStream.Write(metaDataLengthBytes, 0, metaDataLengthBytes.Length);
            state.NetworkStream.Write(metaDataBytes, 0, metaDataBytes.Length);
            state.AllDone=new ManualResetEvent(false);
            state.PreEvent=new ManualResetEvent(false);

            if (state.NetworkStream.CanWrite)
            {
                fileStream.Seek(state.Position + state.FileBlockMetaData.Offset, SeekOrigin.Begin);
                int length = fileStream.Read(state.Buffer, 0, state.BufferSize);
                if (length < state.BufferSize)
                    state.BufferSize = length;
                state.Position = length;
                if(eventArgs==null) eventArgs=new UploadProgressChangedEventArgs();
                if (UploadProgressBegin != null&&eventArgs.HasWrite==0)
                {
                    eventArgs.HasWrite = 0;
                    eventArgs.Length = fileStream.Length;
                    UploadProgressBegin(state, eventArgs);
                }
                //当文件流还没读完时，继续读文件流
                if (state.Position < state.FileBlockMetaData.Length)
                {
                    state.FileStream.BeginRead(state.PreBuffer, 0, state.PreBufferSize,
                                                       PreReadCallBack,
                                                       state);
                }
                state.NetworkStream.BeginWrite(state.Buffer, 0, state.BufferSize, WriteCallBack, state);
                if (UploadProgressChanged != null)
                {
                    eventArgs.HasWrite += state.BufferSize;
                    UploadProgressChanged(state, eventArgs);
                }
                state.AllDone.WaitOne();
            }
        }

        private void PreReadCallBack(IAsyncResult result)
        {
            var state = result.AsyncState as CustomTcpState;
            state.PreBufferSize = state.FileStream.EndRead(result);
            state.PreEvent.Set();
        }

        /// <summary>
        /// 异步写入的回调函数
        /// </summary>
        /// <param name="result"></param>
        public void WriteCallBack(IAsyncResult result)
        {
            var state = result.AsyncState as CustomTcpState;
            Debug.Assert(state != null, "Can not get CustomTcpState !");
            state.AllDone.Set();
            var fileStream = state.FileStream;
            var networkStream = state.NetworkStream;
            if (null == networkStream || null == fileStream || !networkStream.CanWrite) return;
            networkStream.EndWrite(result);

            if (fileStream.CanRead && state.Position<state.FileBlockMetaData.Length && networkStream.CanWrite)
            {
                //如果还有未读完的数据，就等待异步预读那里读完
                if (state.Position < state.FileBlockMetaData.Length)
                {
                    state.PreEvent.WaitOne();
                    state.PreEvent.Reset();
                    state.PreBuffer.CopyTo(state.Buffer,0);   //将预读到的数据，复制到当前要往网络流里写的缓存中
                    state.BufferSize = state.PreBufferSize;
                }
                state.Position += state.PreBufferSize;      //更新当前文件流读到的位置  
                if (state.FileBlockMetaData.Length - state.Position < state.BufferSize)
                {
                    state.PreBufferSize = Convert.ToInt32(state.FileBlockMetaData.Length - state.Position);
                    LogUtil.LogInfo(String.Format("PreBufferSize change to {0}", state.PreBufferSize));
                }
                //还有剩余的数据没有读完时，继续预读
                if (state.Position < state.FileBlockMetaData.Length)
                {
                    state.FileStream.Seek(state.Position + state.FileBlockMetaData.Offset, SeekOrigin.Begin);
                    state.FileStream.BeginRead(state.PreBuffer, 0, state.PreBufferSize,
                                                       PreReadCallBack, state);
                }
                networkStream.BeginWrite(state.Buffer, 0, state.BufferSize, WriteCallBack, state);
                if (UploadProgressChanged != null)
                {
                    eventArgs.HasWrite += state.BufferSize;
                    UploadProgressChanged(state, eventArgs);
                }
                //networkStream.BeginWrite(state.Buffer, 0, flag, WriteCallBack, state);
                //var flag = fileStream.Read(state.Buffer, 0, state.BufferSize);
                //state.Position += flag;
                state.AllDone.WaitOne();
            }
            else if(state.Position==state.FileBlockMetaData.Length)
            {   
                fileStream.Close();
                state.NetworkStream.Close();
                if (eventArgs.HasWrite == eventArgs.Length&&UploadProgressCompleted!=null)
                {
                    UploadProgressCompleted(state, eventArgs);
                }
            }
        }
    }
}